package com.knowledge.review.concurrencyReview.initialThreadPool;

import lombok.Data;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author XFW
 * @Date 13:27 2024/6/14
 * @description initialThreadPool
 **/
public class initialThreadPool {
    public static void main(String[] args) {
        /**
         * 可重用固定线程数的线程池
         * 核心线程数=最大线程数
         * 无需超时时间,只需要设置核心线程数与threadFactory重命名线程即可
         * 使用 LinkedBlockingQueue 阻塞队列
         * 适用于任务量已知,相对耗时的任务
         */
        ExecutorService executorService = Executors.newFixedThreadPool(100, new ThreadFactory() {
            private final AtomicInteger atomicInteger = new AtomicInteger(1);

            @Override
            public Thread newThread(@NotNull Runnable r) {
                return new Thread(r, "newFixedThreadPool~myThread=" + atomicInteger.getAndIncrement());
            }
        });

        /**
         * 根据需要创建新线程的线程池
         * 核心线程数为0,最大线程数为Integer.MAX_VALUE,所以全是救急线程
         * 生存时间为60秒,执行完毕自动释放
         * 队列使用 SynchronOusQueue,没有容量,相当于一手交钱一手交货.
         * 适合任务数比较密集,执行时间短的场景
         */
        ExecutorService executorService1 = Executors.newCachedThreadPool(new ThreadFactory() {
            private final AtomicInteger atomicInteger = new AtomicInteger(1);

            @Override
            public Thread newThread(@NotNull Runnable r) {
                return new Thread(r, "newCachedThreadPool~myThread=" + atomicInteger.getAndIncrement());
            }
        });

        /**
         * 使用单个worker线程的线程池
         * 多个任务排队进行,线程数为 1 ,任务超过 1 时,放入无界队列中
         * 队列使用 LinkedBlockingQueue
         * 任务执行完也不会释放,需要手动释放.
         * 任务执行失败没有补救措施,不可再用
         */
        ExecutorService executorService2 = Executors.newSingleThreadExecutor(new ThreadFactory() {
            private final AtomicInteger atomicInteger = new AtomicInteger(1);

            @Override
            public Thread newThread(@NotNull Runnable r) {
                return new Thread(r, "newSingleThreadExecutor~myThread=" + atomicInteger.getAndIncrement());
            }
        });

        for (int i = 0; i < 21; i++) {
            executorService.execute(() -> {
                System.out.println("ThreadName = " + Thread.currentThread().getName());
            });
        }

        //线程状态变为 stop,不会再接收新任务,将队列中的任务返回,
        // 并使用interrupt方法中断正在执行的任务,相当于全部任务中断.
        executorService.shutdownNow();
        //线程状态变为 shutdown,不会再接收新任务,但等待队列和已提交的任务会执行完.
        executorService2.shutdown();

    }

}

/**
 * 线程的队列一般分为直接提交队列，有界任务队列，无界任务队列，优先任务队列
 */

//
//
/*
 *直接提交队列，我们使用SynchronousQueue（非公平访问策略），默认为false，它没有容量，没执行一个插入操作就会出现阻塞，需要在执行一个删除操作
 * 才会被唤醒，繁殖每一个删除操作也都要等待对应的插入操作
 *
 * 结论：
 *      使用SynchronousQueue队列，提交的任务不会被保存，总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize，
 *      则尝试创建新的进程，如果达到maximumPoolSize设置的最大值，则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来，
 *      而是会被马上执行，在这种情况下，你需要对你程序的并发量有个准确的评估，才能设置合适的maximumPoolSize数量，否则很容易就会执行拒绝策略；
 * */
class ThreadPool0 {

    public static void main(String[] args) {
        //maximumPoolSize设置为2 ，拒绝策略为AbortPolice策略，直接抛出异常
        ExecutorService pool = new ThreadPoolExecutor(1,
                2,
                1000,
                TimeUnit.MILLISECONDS,
                new SynchronousQueue<>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 3; i++) {
            pool.execute(new ThreadTask0());
        }

        pool.shutdown();


        /*
         * 有界的任务队列~有界的任务队列可以使用ArrayBlockingQueue实现
         * 总结：
         *      使用ArrayBlockingQueue有界任务队列，若有新的任务需要执行时，线程池会创建新的线程，直到创建的线程数量达到corePoolSize时，
         *      则会将新的任务加入到等待队列中。若等待队列已满，即超过ArrayBlockingQueue初始化的容量，则继续创建线程，
         *      直到线程数量达到maximumPoolSize设置的最大线程数量，若大于maximumPoolSize，则执行拒绝策略。在这种情况下，
         *      线程数量的上限与有界任务队列的状态有直接关系，如果有界队列初始容量较大或者没有达到超负荷的状态，线程数将一直维持在corePoolSize以下，
         *      反之当任务队列已满时，则会以maximumPoolSize为最大线程数上限
         * */
        ExecutorService pool1 = new ThreadPoolExecutor(1,
                2,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 13; i++) {
            pool1.execute(new ThreadTask0());
        }
        pool1.shutdown();
    }

    /*
     * 无界的任务队列：有界任务队列可以使用LinkedBlockingQueue实现
     * 总结：
     *   使用无界任务队列，线程池的任务队列可以无限制的添加新的任务，而线程池创建的最大线程数量就是你corePoolSize设置的数量，
     *   也就是说在这种情况下maximumPoolSize这个参数是无效的，哪怕你的任务队列中缓存了很多未执行的任务，当线程池的线程数达到corePoolSize后，
     *   就不会再增加了；若后续有新的任务加入，则直接进入队列等待，当使用这种任务队列模式时，一定要注意你任务提交与处理之间的协调与控制，不然会出
     *   现队列中的任务由于无法及时处理导致一直增长，直到最后资源耗尽的问题
     * */

    ExecutorService pool2 = new ThreadPoolExecutor(1,
            2,
            1000,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
}

class ThreadTask0 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

/*
 * 优先任务队列：优先任务队列通过PriorityBlockingQueue实现
 *  总结：
 *
 * */
class ThreadPool1 {

    public static void main(String[] args) {
        //优先任务队列
        ExecutorService pool = new ThreadPoolExecutor(1,
                2,
                1000,
                TimeUnit.MILLISECONDS,
                new PriorityBlockingQueue<>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 20; i++) {
            pool.execute(new ThreadTask1(i));
        }
    }
}

@Data
class ThreadTask1 implements Runnable, Comparable<ThreadTask1> {

    private int priority;

    public ThreadTask1() {

    }

    public ThreadTask1(int priority) {
        this.priority = priority;
    }

    //当前对象和其他对象做比较，当前优先级大就返回-1，优先级小就返回1,值越小优先级越高
    @Override
    public int compareTo(ThreadTask1 o) {
        return this.priority > o.priority ? -1 : 1;
    }

    @Override
    public void run() {
        try {
            //让线程阻塞，使后续任务进入缓存队列
            Thread.sleep(1000);
            System.out.println("priority:" + this.priority + ",ThreadName:" + Thread.currentThread().getName());
        } catch (InterruptedException ignored) {
        }

    }
}


/*
 * 拒绝策略
 *   一般我们创建线程池时，为防止资源被耗尽，任务队列都会选择创建有界任务队列，但种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大
 *   线程数时，这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略，来处理线程池"超载"的情况。
 *   ThreadPoolExecutor自带的拒绝策略如下：
 *           1、AbortPolicy策略：该策略会直接抛出异常，阻止系统正常工作；
 *           2、CallerRunsPolicy策略：如果线程池的线程数量达到上限，该策略会把任务队列中的任务放在调用者线程当中运行；
 *           3、DiscardOledestPolicy策略：该策略会丢弃任务队列中最老的一个任务，也就是当前任务队列中最先被添加进去的，马上要被执行的那个任务，并尝试再次提交；
 *           4、DiscardPolicy策略：该策略会默默丢弃无法处理的任务，不予任何处理。当然使用此策略，业务场景中需允许任务的丢失；
 *   以上内置的策略均实现了RejectedExecutionHandler接口，当然你也可以自己扩展RejectedExecutionHandler接口，定义自己的拒绝策略
 *
 * */
class ThreadPool {
    public static void main(String[] args) {
        //自定义拒绝策略
        ExecutorService pool = new ThreadPoolExecutor(1,
                2,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(), (r, executor) -> System.out.println(r.toString() + "执行了拒绝策略"));

        for (int i = 0; i < 10; i++) {
            pool.execute(new ThreadTask());
        }
    }
}

class ThreadTask implements Runnable {
    @Override
    public void run() {
        try {
            //让线程阻塞，使后续任务进入缓存队列
            Thread.sleep(1000);
            System.out.println("ThreadName:" + Thread.currentThread().getName() + "------" + Thread.currentThread().getState());
        } catch (InterruptedException ignored) {
        }

    }
}

class ThreadPool2 {
    public static void main(String[] args) throws InterruptedException {
        //实现自定义接口
        //线程命名
        ExecutorService pool = new ThreadPoolExecutor(2,
                4,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(5),
                r -> {
                    System.out.println("线程" + r.hashCode() + "创建");
                    //线程命名
                    return new Thread(r, "threadPool" + r.hashCode());
                }, new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void beforeExecute(Thread t, Runnable r) {
                System.out.println("准备执行：" + ((ThreadTask2) r).getTaskName());
            }

            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                System.out.println("执行完毕：" + ((ThreadTask2) r).getTaskName());
            }

            @Override
            protected void terminated() {
                System.out.println("线程池退出");
            }
        };

        for (int i = 0; i < 10; i++) {
            pool.execute(new ThreadTask2("Task" + i));
        }
        pool.shutdown();
    }
}

@Data
class ThreadTask2 implements Runnable {
    private String taskName;

    public ThreadTask2(String name) {
        this.setTaskName(name);
    }

    @Override
    public void run() {
        //输出执行线程的名称
        System.out.println("TaskName" + this.getTaskName() + "---ThreadName:" + Thread.currentThread().getName());
    }
}

class Test {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(calc());
    }

    private AtomicLong  count = new AtomicLong(0);


    private void add10K() {
        int idx = 0;
        while (idx++ < 10000) {
            count.incrementAndGet();
        }
    }

    public static AtomicLong calc() throws InterruptedException {
        final Test test = new Test();
        // 创建两个线程，执行add()操作
        Thread th1 = new Thread(test::add10K);
        Thread th2 = new Thread(test::add10K);
        // 启动两个线程
        th1.start();
        th2.start();
        // 等待两个线程执行结束
        th1.join();
        th2.join();
        return test.count;
    }
}

class ThreadTest {

    private static class T1 extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T1");
            }
        }
    }

    public static void main(String[] args) {
//        new T1().run();
        new T1().start();
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignored) {
            }
            System.out.println("Main");
        }

    }

}